﻿using System;
using System.Linq;

using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core;
using mvcForum.Core.Events;
using mvcForum.Core.Interfaces.Events;
using mvcForum.Web.Extensions;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Core.Interfaces.Data;

namespace mvcForum.Web.Events {

	/// <summary>
	/// The class handles the changes needed to be done when the value of a PostFlag or TopicFlag changes,
	/// ie. updating post/topic count etc.
	/// </summary>
	public class FlagUpdatedEventListener : IAsyncEventListener<PostFlagUpdatedEvent>,
											IAsyncEventListener<TopicFlagUpdatedEvent> {
		private readonly ILogger logger;
		private readonly IAsyncTask task;
		private readonly IConfiguration config;
		private readonly IContext context;

		public FlagUpdatedEventListener(ILogger logger, IContext context, IAsyncTask task, IConfiguration config) {
			this.logger = logger;
			this.task = task;
			this.config = config;
			this.context = context;
		}

		public void Queue(PostFlagUpdatedEvent payload) {
			this.logger.Log(EventType.Debug, String.Format("Post {0} is queued.", payload.PostId));
			// TODO: Delay!!
			this.task.Execute(this, payload, 3);
		}

		public void Queue(TopicFlagUpdatedEvent payload) {
			this.logger.Log(EventType.Debug, String.Format("Topic {0} is queued.", payload.TopicId));
			// TODO: Delay!!
			this.task.Execute(this, payload, 3);
		}

		public void Handle(Object payload) {
			if (payload is PostFlagUpdatedEvent) {
				this.Handle((PostFlagUpdatedEvent)payload);
			}
			else if (payload is TopicFlagUpdatedEvent) {
				this.Handle((TopicFlagUpdatedEvent)payload);
			}
			else {
				throw new ApplicationException("Unknown payload!");
			}
		}

		/// <summary>
		/// Method for handling a PostFlag change.
		/// </summary>
		/// <param name="payload">The event payload.</param>
		public void Handle(PostFlagUpdatedEvent payload) {
			// Let's get the updated post.
			Post post = this.context.GetRepository<Post>().Read(payload.PostId);
			// Log info!
			this.logger.Log(EventType.Debug, String.Format("A post '{1}' was updated, time to index it ({0}).", post.Id, post.Subject));
			// Let's make sure the PostCount property is correct on the parent topic.
			post.Topic.PostCount = post.Topic.Posts.Visible(this.config).Count() - 1;
			// Save then changes!
			this.context.SaveChanges();
			// And update the forum (and any parent forums).
			NewAndUpdatedContentEventListener.UpdateForum(post.Topic.Forum, this.context);
		}

		/// <summary>
		/// Method for handling a TopicFlag change.
		/// </summary>
		/// <param name="payload">The event payload.</param>
		public void Handle(TopicFlagUpdatedEvent payload) {
			// Let's get the updated topic.
			Topic topic = this.context.GetRepository<Topic>().Read(payload.TopicId);
			// Log info!
			this.logger.Log(EventType.Debug, String.Format("A topic '{1}' was updated, time to index it ({0}).", topic.Id, topic.Title));
			// Has the topic been changed in any significant way?
			if (topic.Flag != payload.OriginalFlag) {
				// Yes, state change, we might have to update the topic/post count etc.
				if ((payload.OriginalFlag == TopicFlag.Locked && topic.Flag == TopicFlag.None) ||
					(payload.OriginalFlag == TopicFlag.None && topic.Flag == TopicFlag.Locked) ||
					(payload.OriginalFlag == TopicFlag.Quarantined && topic.Flag == TopicFlag.Deleted) ||
					(payload.OriginalFlag == TopicFlag.Deleted && topic.Flag == TopicFlag.Quarantined)) {

					// Minor change, nothing we need to worry about! Doesn't change the latest topic/post info!
					// The change from old state to new doesn't change the visibility of the topic, so no need to worry!
				}
				else {
					// Get the number of not deleted and not quarantined posts (first is the topic!)
					Int32 posts = topic.Posts.Visible(this.config).Count() - 1;
					if (payload.OriginalFlag == TopicFlag.Deleted || payload.OriginalFlag == TopicFlag.Quarantined) {
						// Was deleted/quarantined, not anymore! Update forum (and any parent forums).
						NewAndUpdatedContentEventListener.UpdateForum(topic.Forum, this.context);
					}
					else {
						// Was visible, not anymore! Update forum (and any parent forums).
						NewAndUpdatedContentEventListener.UpdateForum(topic.Forum, this.context);
					}
					// No need to save the changes, that's already handled in the UpdateForum method!!
				}
			}
		}

		public Byte Priority {
			get {
				return (Byte)EventPriority.Lowest;
			}
		}

		public Boolean UniqueEvent {
			get {
				return false;
			}
		}

		public Boolean RunAsynchronously {
			get {
				// TODO: Config !?
				return false;
			}
		}
	}
}